﻿// 2023/3/10: 首个版本
// 2023/5/10: 协议文件选择默认筛选改为全部文件类型
// 2023/9/22: 使用PassParent给ShowDialog传参
// 2023/9/25: 改为直接输入Icon对象
// 2023/10/9: 添加英文注释

using System;
using System.Collections.Generic;
using System.Linq;
using ASEva;
using ASEva.Samples;
using ASEva.UIEto;
using Eto.Forms;
using Eto.Drawing;

namespace CommonSource
{
    class BusProtocolChooser : DialogPanel
    {
        public BusProtocolChooser(bool canSelect, BusProtocolFileID[] originSelection, Icon icon = null)
        {
            if (originSelection == null) originSelection = new BusProtocolFileID[0];
            this.originSelection = originSelection;

            Icon = icon;
            Title = chinese ? "总线协议文件库" : "Bus Protocol Files";
            SetResizableMode(800, 450, 800, 450);

            var mainLayout = this.SetContentAsColumnLayout();
            var listLayout = mainLayout.AddRowLayout(true, 8, VerticalAlignment.Stretch);
            var groupInfo = mainLayout.AddGroupBox(chinese ? "文件信息" : "Information");

            if (canSelect) checkListBox = listLayout.AddControl(new CheckableListBox(), true) as CheckableListBox;
            else listBox = listLayout.AddControl(new ListBox(), true) as ListBox;
            var buttonsLayout = listLayout.AddColumnLayout(false, 150, 0, 8);

            var buttonAdd = buttonsLayout.AddButton(chinese ? "添加文件至协议库" : "Add file to library");
            buttonRemove = buttonsLayout.AddButton(chinese ? "从协议库移除文件" : "Remove from library");
            buttonRedirect = buttonsLayout.AddButton(chinese ? "重定向文件路径" : "Redirect file path");
            buttonRemove.Enabled = buttonRedirect.Enabled = false;
            
            Button buttonCancel = null;
            if (canSelect)
            {
                buttonsLayout.AddSpace();
                buttonCancel = buttonsLayout.AddButton(chinese ? "取消" : "Cancel");
                buttonSelect = buttonsLayout.AddButton(chinese ? "选择" : "Select");
                buttonSelect.Enabled = false;
            }

            var infoLayout = groupInfo.SetContentAsTableLayout();
            var row = infoLayout.AddRow(true);
            row.AddLabel("MD5:");
            labelMD5 = row.AddLabel("--");
            row = infoLayout.AddRow(true);
            row.AddLabel(chinese ? "路径:" : "Path:");
            labelPath = row.AddLabel("--");

            updateList();

            if (listBox != null)
            {
                listBox.SelectedIndexChanged += delegate
                {
                    if (listBox.SelectedIndex < 0) return;
                    updateControls(new BusProtocolFileID[] { idList[listBox.SelectedIndex] });
                };
            }

            if (checkListBox != null)
            {
                checkListBox.ItemClicked += delegate
                {
                    var indices = checkListBox.GetCheckedIndices();
                    var ids = new BusProtocolFileID[indices.Length];
                    for (int i = 0; i < ids.Length; i++) ids[i] = idList[indices[i]];
                    updateControls(ids);
                };
            }

            buttonAdd.Click += delegate
            {
                var dialog = new OpenFileDialog();
                dialog.Title = chinese ? "协议文件选择" : "Please select a protocol file";
                dialog.MultiSelect = true;
                dialog.Filters.Add(new FileFilter(chinese ? "全部支持的文件类型 (*.dbc, *.arxml, *.xml)" : "All Supported Files (*.dbc, *.arxml, *.xml)", ".dbc", ".arxml", ".xml"));
                dialog.Filters.Add(new FileFilter(chinese ? "CAN协议文件 (*.dbc)" : "CAN Database File (*.dbc)", ".dbc"));
                dialog.Filters.Add(new FileFilter(chinese ? "汽车开放系统架构XML (*.arxml)" : "AutoSAR XML (*.arxml)", ".arxml"));
                dialog.Filters.Add(new FileFilter(chinese ? "现场总线交互格式(FIBEX) (*.xml)" : "Fieldbus Exchange Format (FIBEX) (*.xml)", ".xml"));
                if (dialog.ShowDialog(App.PassParent(this)) != DialogResult.Ok) return;

                foreach (var file in dialog.Filenames)
                {
                    var result = Agency.AddBusProtocolFile(file);
                    switch (result)
                    {
                        case AddBusProtocolResult.AlreadyAdded:
                            Agency.PopupNotice((chinese ? "文件" : "The file ") + file + (chinese ? "已在库中" : " is already in library."));
                            break;
                        case AddBusProtocolResult.CalculateMD5Failed:
                            var row1 = (chinese ? "文件" : "The file ") + file + (chinese ? "正在被使用。" : " is being used.");
                            var row2 = chinese ? "或者此操作系统不支持MD5。" : "Or MD5 is not supported by the OS."; // Occurs mainly in Windows, here's the solution / 主要在Windows下出现，解决方法：HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\FipsAlgorithmPolicy->enable=0
                            Agency.PopupError(row1 + "\n" + row2);
                            break;
                    }
                }

                var checkedIDs = getCheckedIDs();
                updateList(checkedIDs);
                updateControls(checkedIDs);
            };

            buttonRemove.Click += delegate
            {
                var idsToRemove = new List<BusProtocolFileID>();
                if (listBox != null)
                {
                    if (listBox.SelectedIndex >= 0) idsToRemove.Add(idList[listBox.SelectedIndex]);
                }
                if (checkListBox != null)
                {
                    foreach (var index in checkListBox.GetCheckedIndices())
                    {
                        idsToRemove.Add(idList[index]);
                    }
                }

                foreach (var id in idsToRemove)
                {
                    Agency.RemoveBusProtocolFile(id);
                }

                updateList();
                updateControls(null);
            };

            buttonRedirect.Click += delegate
            {
                BusProtocolFileID selected = null;
                if (listBox != null) selected = idList[listBox.SelectedIndex];
                if (checkListBox != null) selected = idList[checkListBox.GetCheckedIndices()[0]];

                if (selected.FileName.Contains("@"))
                {
                    Agency.PopupError(chinese ? "不支持带通道的协议。" : "Protocols with channel are unsupported.");
                    return;
                }

                var dialog = new OpenFileDialog();
                dialog.Title = chinese ? "协议文件选择" : "Please select a protocol file";
                dialog.Filters.Add(new FileFilter(chinese ? "CAN协议文件 (*.dbc)" : "CAN Database File(*.dbc)", ".dbc"));
                dialog.Filters.Add(new FileFilter(chinese ? "汽车开放系统架构XML (*.arxml)" : "AutoSAR XML (*.arxml)", ".arxml"));
                dialog.Filters.Add(new FileFilter(chinese ? "现场总线交互格式(FIBEX) (*.xml)" : "Fieldbus Exchange Format (FIBEX) (*.xml)", ".xml"));
                if (dialog.ShowDialog(App.PassParent(this)) != DialogResult.Ok) return;

                var file = dialog.FileName;
                if (!Agency.UpdateBusProtocolFilePath(selected, file))
                {
                    Agency.PopupError(chinese ? "文件名或MD5不匹配。" : "The file name or the MD5 doesn't match.");
                    return;
                }

                updateList(null);
                updateControls(null);
            };

            if (buttonCancel != null)
            {
                buttonCancel.Click += delegate
                {
                    Close();
                };
            }

            if (buttonSelect != null)
            {
                buttonSelect.Click += delegate
                {
                    selectedResult = getCheckedIDs();
                    BoolResult = true;
                    Close();
                };
            }
        }

        public BusProtocolFileID[] SelectedProtocolFiles
        {
            get { return selectedResult; }
        }
        
        private String genItemText(BusProtocolFileID id, out bool checkable)
        {
            checkable = false;
            String postfix = "";
            var state = Agency.GetBusProtocolFileState(id);
            if (state != BusProtocolFileState.OK)
            {
                postfix += " (ERROR)";
                checkable = true;
            }
            else if (originSelection.Contains(id)) postfix += " (SELECTED)";
            else checkable = true;
            return id.FileName + postfix;
        }

        private void updateList(BusProtocolFileID[] checkedIDs = null)
        {
            if (listBox != null) listBox.Items.Clear();
            if (checkListBox != null) checkListBox.RemoveAllItems();

            idList = Agency.GetBusProtocolFileIDList();
            foreach (var id in idList)
            {
                bool checkable;
                var itemText = genItemText(id, out checkable);
                if (listBox != null) listBox.Items.Add(itemText);
                if (checkListBox != null) checkListBox.AddItem(itemText, checkedIDs != null && checkedIDs.Contains(id), checkable);
            }
        }

        private void updateControls(BusProtocolFileID[] selectedIDs)
        {
            if (selectedIDs == null || selectedIDs.Length == 0)
            {
                labelPath.Text = "--";
                labelPath.TextColor = Colors.Black;
                labelMD5.Text = "--";
                labelMD5.TextColor = Colors.Black;

                buttonRemove.Enabled = false;
                buttonRedirect.Enabled = false;
                if (buttonSelect != null) buttonSelect.Enabled = false;
            }
            else if (selectedIDs.Length > 1)
            {
                labelPath.Text = "--";
                labelPath.TextColor = Colors.Black;
                labelMD5.Text = "--";
                labelMD5.TextColor = Colors.Black;

                bool canRemove = true, canSelect = true;
                foreach (var id in selectedIDs)
                {
                    var state = Agency.GetBusProtocolFileState(id);
                    if (state == BusProtocolFileState.OK && originSelection.Contains(id)) canRemove = false;
                    if (state != BusProtocolFileState.OK || originSelection.Contains(id)) canSelect = false;
                }
                buttonRemove.Enabled = canRemove;
                buttonRedirect.Enabled = false;
                if (buttonSelect != null) buttonSelect.Enabled = canSelect;
            }
            else
            {
                var id = selectedIDs[0];
                var state = Agency.GetBusProtocolFileState(id);

                labelPath.Text = Agency.GetBusProtocolFilePath(id);
                labelPath.TextColor = state == BusProtocolFileState.FileNotExist ? Colors.Red : Colors.Black;
                labelMD5.Text = id.MD5;
                labelMD5.TextColor = state == BusProtocolFileState.MD5NotCorrect ? Colors.Red : Colors.Black;

                buttonRemove.Enabled = state != BusProtocolFileState.OK || !originSelection.Contains(id);
                buttonRedirect.Enabled = state != BusProtocolFileState.OK;
                if (buttonSelect != null) buttonSelect.Enabled = state == BusProtocolFileState.OK && !originSelection.Contains(id);
            }
        }

        private BusProtocolFileID[] getCheckedIDs()
        {
            var selectedList = new List<BusProtocolFileID>();
            if (checkListBox != null)
            {
                foreach (var index in checkListBox.GetCheckedIndices())
                {
                    selectedList.Add(idList[index]);
                }
            }
            return selectedList.ToArray();
        }

        private ListBox listBox;
        private CheckableListBox checkListBox;
        private Label labelPath, labelMD5;
        private Button buttonRemove, buttonRedirect, buttonSelect;

        private BusProtocolFileID[] idList;
        private BusProtocolFileID[] originSelection;
        private BusProtocolFileID[] selectedResult;

        private bool chinese = Agency.GetAppLanguage() == "ch";
    }
}